using Agron.Data.Contracts;
using Agron.Security.ClientLayer;
using Agron.Services.Contracts;

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ComponentModel;
using System.Windows.Forms;


namespace Agron.Services.Broadcasting
{
    /// <summary>
    /// Implementation of the behavior for the <see cref="IBroadcastingService"/> service contract.
    /// </summary>
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.PerSession)]
    public class BroadcastingService : IBroadcastingService, IDisposable
    {
        private static List<IBroadcastingServiceEvents> _callbackList = new List<IBroadcastingServiceEvents>();

        private MouseHook _mouseHook;
        private ServiceHost _serviceHost;

        /// <summary>
        /// Initializes a new instance of the <see cref="BroadcastingService"/> class.
        /// </summary>
        public BroadcastingService() { }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="BroadcastingService"/> is reclaimed by garbage collection.
        /// </summary>
        ~BroadcastingService()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// Starts the inner <see cref="ServiceHost"/>.
        /// </summary>
        public void StartHost()
        {
            try
            {
                // Initialize Mouse Hook
                this._mouseHook = MouseHook.Initialize();
                this._mouseHook.MouseDown += new EventHandler<System.Windows.Forms.MouseEventArgs>(OnMouseHookMouseDown);
                this._mouseHook.MouseMove += new EventHandler<System.Windows.Forms.MouseEventArgs>(OnMouseHookMouseMove);
                this._mouseHook.MouseUp += new EventHandler<System.Windows.Forms.MouseEventArgs>(OnMouseHookMouseUp);

                // Initialize Service Host
                this._serviceHost = new ServiceHost(typeof(BroadcastingService));
                this._serviceHost.Open();
            }
            catch (Win32Exception win32exception)
            {
                throw new InvalidOperationException(Properties.Resources.BroadcastingServiceMouseHookError, win32exception);
            }
        }

        /// <summary>
        /// Stops the contained <see cref="ServiceHost"/>.
        /// </summary>
        public void StopHost()
        {
            this._serviceHost.Close();
            this._mouseHook.Dispose();
        }

        #region IBroadcastingService Implementation

        /// <summary>
        /// Subscribes a caller to the Broadcasting Service host.
        /// </summary>
        [OperationBehavior]
        public void Subscribe()
        {
            IBroadcastingServiceEvents callback = OperationContext.Current.GetCallbackChannel<IBroadcastingServiceEvents>();
            if ((null != callback) && (!_callbackList.Contains(callback)))
                _callbackList.Add(callback);
        }

        /// <summary>
        /// Unsubscribes a caller from the Broadcasting Service host.
        /// </summary>
        [OperationBehavior]
        public void Unsubscribe()
        {
            IBroadcastingServiceEvents callback = OperationContext.Current.GetCallbackChannel<IBroadcastingServiceEvents>();
            if ((null != callback) && (_callbackList.Contains(callback)))
                _callbackList.Remove(callback);
        }

        /// <summary>
        /// Notifies that the mouse's left button is up.
        /// </summary>
        /// <param name="x">An <see cref="Int32"/> that contains the x-coordinate of the mouse when the event happened.</param>
        /// <param name="y">An <see cref="Int32"/> that contains the y-coordinate of the mouse when the event happened.</param>
        [OperationBehavior]
        public void NotifyLeftButtonDown(int x, int y)
        {
            UserProfile userProfile = AgronIdentity.GetCurrent().Profile;
            if (null != userProfile)
            {
                _callbackList.ForEach(delegate(IBroadcastingServiceEvents callback)
                {
                    try
                    {
                        callback.LeftButtonDown(userProfile.UserId, userProfile.DisplayName, x, y);
                    }
                    catch (Exception)
                    {
                        // Do nothing for now...
                    }
                });
            }
        }

        /// <summary>
        /// Notifies that the mouse's left button is up.
        /// </summary>
        /// <param name="x">An <see cref="Int32"/> that contains the x-coordinate of the mouse when the event happened.</param>
        /// <param name="y">An <see cref="Int32"/> that contains the y-coordinate of the mouse when the event happened.</param>
        [OperationBehavior]
        public void NotifyLeftButtonUp(int x, int y)
        {
            UserProfile userProfile = AgronIdentity.GetCurrent().Profile;
            if (null != userProfile)
            {
                _callbackList.ForEach(delegate(IBroadcastingServiceEvents callback)
                {
                    try
                    {
                        callback.LeftButtonUp(userProfile.UserId, userProfile.DisplayName, x, y);
                    }
                    catch (Exception)
                    {
                        // Do nothing for now...
                    }
                });
            }
        }

        /// <summary>
        /// Notifies that the mouse has moved its position.
        /// </summary>
        /// <param name="x">An <see cref="Int32"/> that contains the x-coordinate of the mouse when the event happened.</param>
        /// <param name="y">An <see cref="Int32"/> that contains the y-coordinate of the mouse when the event happened.</param>
        [OperationBehavior]
        public void NotifyMove(int x, int y)
        {
            UserProfile userProfile = AgronIdentity.GetCurrent().Profile;
            if (null != userProfile)
            {
                _callbackList.ForEach(delegate(IBroadcastingServiceEvents callback)
                {
                    try
                    {
                        callback.MouseMove(userProfile.UserId, userProfile.DisplayName, x, y);
                    }
                    catch (Exception)
                    {
                        // Do nothing for now...
                    }
                });
            }
        }

        /// <summary>
        /// Notifies that the mouse's right button is down.
        /// </summary>
        /// <param name="x">An <see cref="Int32"/> that contains the x-coordinate of the mouse when the event happened.</param>
        /// <param name="y">An <see cref="Int32"/> that contains the y-coordinate of the mouse when the event happened.</param>
        [OperationBehavior]
        public void NotifyRightButtonDown(int x, int y)
        {
            UserProfile userProfile = AgronIdentity.GetCurrent().Profile;
            if (null != userProfile)
            {
                _callbackList.ForEach(delegate(IBroadcastingServiceEvents callback)
                {
                    try
                    {
                        callback.RightButtonDown(userProfile.UserId, userProfile.DisplayName, x, y);
                    }
                    catch (Exception)
                    {
                        // Do nothing for now...
                    }
                });
            }
        }

        /// <summary>
        /// Notifies that the mouse's right button is up.
        /// </summary>
        /// <param name="x">An <see cref="Int32"/> that contains the x-coordinate of the mouse when the event happened.</param>
        /// <param name="y">An <see cref="Int32"/> that contains the y-coordinate of the mouse when the event happened.</param>
        [OperationBehavior]
        public void NotifyRightButtonUp(int x, int y)
        {
            UserProfile userProfile = AgronIdentity.GetCurrent().Profile;
            if (null != userProfile)
            {
                _callbackList.ForEach(delegate(IBroadcastingServiceEvents callback)
                {
                    try
                    {
                        callback.RightButtonUp(userProfile.UserId, userProfile.DisplayName, x, y);
                    }
                    catch (Exception)
                    {
                        // Do nothing for now...
                    }
                });
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (null != this._mouseHook)
                    this._mouseHook.Dispose();

                if ((null != this._serviceHost) && (CommunicationState.Opened == this._serviceHost.State))
                    this._serviceHost.Close();
            }
        }

        /// <summary>
        /// Handles the MouseUp event of the contained <see cref="MouseHook"/> instance.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        protected virtual void OnMouseHookMouseUp(object sender, MouseEventArgs e)
        {
            if (MouseButtons.Left == e.Button)
            {
                this.NotifyLeftButtonUp(e.X, e.Y);
            }
            else if (MouseButtons.Right == e.Button)
            {
                this.NotifyRightButtonUp(e.X, e.Y);
            }
        }

        /// <summary>
        /// Handles the MouseMove event of the contained <see cref="MouseHook"/> instance.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        protected virtual void OnMouseHookMouseMove(object sender, MouseEventArgs e)
        {
            this.NotifyMove(e.X, e.Y);
        }

        /// <summary>
        /// Handles the MouseDown event of the contained <see cref="MouseHook"/> instance.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        protected virtual void OnMouseHookMouseDown(object sender, MouseEventArgs e)
        {
            if (MouseButtons.Left == e.Button)
            {
                this.NotifyLeftButtonDown(e.X, e.Y);
            }
            else if (MouseButtons.Right == e.Button)
            {
                this.NotifyRightButtonDown(e.X, e.Y);
            }
        }
    }
}
